package main

import (
	"bytes"
	"encoding/binary"
	"os"
	"image"
	"image/color"
	"image/draw"
	"fmt"
)

var _ = fmt.Printf // XXX

var devdraw *os.File
var writebuf *bytes.Buffer
var imageId int
var screenId int

func nextImageId() int {
	imageId++
	return imageId
}

func init() {
	buf := make([]byte, 0)
	writebuf = bytes.NewBuffer(buf)
}

func writeLong(i int) {
	binary.Write(writebuf, binary.LittleEndian, int32(i))
}

func writeByte(i uint8) {
	binary.Write(writebuf, binary.LittleEndian, uint8(i))
}

func writeString(s string) {
	writebuf.WriteString(s)
}

func writePoint(p image.Point) {
	writeLong(p.X)
	writeLong(p.Y)
}

func writeRect(r image.Rectangle) {
	writeLong(r.Min.X)
	writeLong(r.Min.Y)
	writeLong(r.Max.X)
	writeLong(r.Max.Y)
}

func writeImage(m image.Image) {
	b := m.Bounds()
	for y := b.Min.Y; y < b.Max.Y; y++ {
		for x := b.Min.X; x < b.Max.X; x++ {
			writeColor(m.At(x, y))
		}
	}
}

func writeColor(c color.Color) {
	r, g, b, _ := c.RGBA()
	//fmt.Printf("writing pixel: X(%d) B(%d) G(%d) R(%d)\n", 255, uint8(b >> 8), uint8(g >> 8), uint8(r >> 8))
	writeByte(255)
	writeByte(uint8(b >> 8))
	writeByte(uint8(g >> 8))
	writeByte(uint8(r >> 8))
}

func send() error {
	_, err := writebuf.WriteTo(devdraw)
	if err != nil {
		writebuf.Reset()
	}
	return err
}

func NamedImage(name string) (int, error) {
	id := nextImageId()
	//fmt.Println("giving image", name, "id of", id)
	writeString("n")
	writeLong(id)
	writeByte(uint8(len(name)))
	writeString(name)
	return id, send()
}

// b id[4] screenid[4] refresh[1] chan[4] repl[1] r[4*4] clipr[4*4] color[4]
func AllocImage(r image.Rectangle, ref uint8, chantype uint, repl uint8, val uint) (int, error) {
	id := nextImageId()
	//fmt.Println("allocating image with id", id, "and screenId", screenId)
	writeString("b")
	writeLong(id)
	writeLong(screenId)
	writeByte(ref)
	writeLong(int(chantype))
	writeByte(repl)
	writeRect(r)
	var clipr image.Rectangle
	if repl != 0 {
		// huge but not infinite, so various offsets will leave it huge, not overflow
		clipr = image.Rect(-0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF)
	} else {
		clipr = r
	}
	writeRect(clipr)
	writeLong(int(val))
	//fmt.Printf("b %d %d %d %d %d %v %v %d\n", id, screenId, ref, int(chantype), repl, r, clipr, val)
	return id, send()
}

func AllocScreen(backingImageId int, fillId int) {
	for try := 1; try<25; try++ {
		writeString("A")
		writeLong(try)
		writeLong(backingImageId)
		writeLong(fillId)
		writeByte(0)
		if err := send(); err == nil {
			//fmt.Println("allocating screen with backing image", backingImageId, "and id", try)
			//fmt.Printf("A %d %d %d %d\n", try, backingImageId, fillId, 0)
			screenId = try
			break
		}
		
	}
}

func AllocImageMix(color1, color3 uint) draw.Image {
	return nil
}

func FlushImage() error {
	writeString("v")
	return send()
}

func FillEllipse(dst draw.Image, c image.Point, a, b int, src draw.Image, sp image.Point) {
}

//L dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] thick[4] srcid[4] sp[2*4]
func DrawLine(dst int, p0, p1 image.Point, end0, end1, thick, src int, sp image.Point) error {
	writeString("L")
	writeLong(dst)
	writePoint(p0)
	writePoint(p1)
	writeLong(end0)
	writeLong(end1)
	writeLong(thick)
	writeLong(src)
	writePoint(sp)
	return send()
}

func ReplaceRectangle(id int, r image.Rectangle, img image.Image) error {
	writeString("y")
	writeLong(id)
	writeRect(r)
	writeImage(img)
	return send()
}